/**
 * 
 */
package business.user;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import service.Service;
import service.Service.ServiceType;
import service.ServiceManager;
import service.resources.Dao;
import bean.Customer;
import bean.LoginCredential;
import bean.ParamBean;
import bean.User;
import bean.UserProfile;
import business.exception.UserLoginException;

import common.Utils;

import data.DaoFactory.DaoName;
import data.ICustomerDao;
import data.IUserDao;
import data.exception.DaoException;

/**
 * User Authentication Service Manager.
 * 
 * @author chuxiaoyuan
 * 
 */
public class UserAuthentication extends ServiceManager {

	/**
	 * IsLogin service.
	 */
	public static final String SERVICE_ISLOGIN = "ISLOGIN";

	/**
	 * Login service.
	 */
	public static final String SERVICE_LOGIN = "LOGIN";

	/**
	 * Logout service.
	 */
	public static final String SERVICE_LOGOUT = "LOGOUT";

	static {
		REGISTER(UserAuthentication.class);
	}

	/**
	 * Constructor.
	 * 
	 * @param request
	 *            the HTTP servlet request.
	 */
	public UserAuthentication(HttpServletRequest request) {
		super(request);
	}

	/**
	 * The User DAO.
	 */
	@Dao(name = DaoName.UserDao)
	private IUserDao userDao;

	/**
	 * The Customer DAO.
	 */
	@Dao(name = DaoName.CustomerDao)
	private ICustomerDao customerDao;

	/**
	 * Test if user is currently login.
	 * 
	 * @return true if it is login.
	 */
	@Service(name = SERVICE_ISLOGIN)
	public boolean isLogin() {

		// Get session.
		HttpSession session = this.getSession();
		if (session == null) {
			return false;
		}

		// Test parameter bean.
		ParamBean param = ParamBean.getParamBean(session);
		if (param == null || param.getUserProfile() == null) {

			// Invalid session.
			return false;
		}

		return true;
	}

	/**
	 * Login using the specified credential.
	 * 
	 * @param login
	 *            the login credential.
	 * @return the valid parameter bean. null if login failed.
	 * @throws UserLoginException
	 *             login error.
	 */
	@Service(name = SERVICE_LOGIN, type = ServiceType.Query)
	public ParamBean login(LoginCredential login) throws UserLoginException {

		// Logout first.
		logout();

		// Validate login credential
		String username = login.getUsername();
		String password = login.getPassword();
		String encryptedPwd;
		try {
			// Encrypt password.
			encryptedPwd = Utils.encryptPassword(password);

		} catch (NoSuchAlgorithmException e) {

			String msg = "Password encryption failure, Login failed.";
			//this.getLogger().error(msg, e);
			throw new UserLoginException(msg, e);

		} catch (UnsupportedEncodingException e) {

			String msg = "Password encryption failure, Login failed.";
			//this.getLogger().error(msg, e);
			throw new UserLoginException(msg, e);
		}
		if (username == null || encryptedPwd == null) {

			// Invalid username or password
			return null;
		}

		// Match user database.
		List<User> users;
		try {
			users = userDao.selectByAccountAndPassword(username, encryptedPwd);
		} catch (DaoException e) {

			String msg = "Login failed: failed to retrieve user information.";
			//this.getLogger().error(msg, e);
			throw new UserLoginException(msg, e);
		}
		if (users == null || users.size() != 1) {

			// User mismatch
			return null;
		}

		// Get user profile information.
		UserProfile profile = new UserProfile();
		User user = users.get(0);
		profile.setUserInfo(user);
		if (profile.isCustomer()) {
			try {
				
				// Retrieve customer info.
				Customer customer = customerDao.selectById(
						profile.getCustomerid());
				profile.setCustomerInfo(customer);
			
			} catch (DaoException e) {
				String msg = 
						"Login failed: failed to retrieve customer information of " 
								+ profile.getCustomerid();
				//this.getLogger().error(msg, e);
				throw new UserLoginException(msg, e);
			}
		}

		// Populate parameter bean
		HttpSession session = this.getSession();
		if (session == null) {
			// Current session is already invalid
			// Create a new one.
			session = this.getRequest().getSession(true);
			if (session == null) {
				throw new UserLoginException("Failed to create session!");
			}
		}
		ParamBean param = ParamBean.newParamBean(session);
		param.setUserProfile(profile);

		return param;
	}

	/**
	 * Do logout.
	 */
	@Service(name = SERVICE_LOGOUT)
	public void logout() {

		if (!isLogin()) {
			return;
		}

		// Logout
		HttpSession session = this.getSession();
		if (session != null) {
			ParamBean.removeParamBean(session);
			session.invalidate();
		}
		return;
	}
	
}
